package com.xc.stock.domain.basic.entity;

import java.math.BigDecimal;

import com.ag.exception.SysTipsException;
import com.ag.utils.BigDecimals;
import com.ag.utils.Jsons;
import com.ag.utils.SysConstant.BooleanEnum;
import com.alibaba.fastjson.JSONObject;
import com.querydsl.core.types.Expression;
import com.querydsl.core.types.dsl.CaseBuilder;
import com.querydsl.jpa.impl.JPAUpdateClause;
import com.xc.stock.constant.AccountActionEnum;
import com.xc.stock.constant.AccountLogType;
import com.xc.stock.constant.Currency;
import com.xc.stock.domain.basic.repository.AccountRepository;
import com.xc.stock.dto.model.AccountOperParam;
import com.xc.stock.dto.model.AccountThumb;
import com.xc.stock.dto.model.TradeAccThumb;
import com.xc.stock.infrastructure.db.po.AccountPO;
import com.xc.stock.infrastructure.db.po.QAccountPO;
import com.xc.stock.utils.Beans;

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;

/**
 * Account 
 *
 * @author xlfd 
 * @email xlfd@gmail.com 
 * @created 2022/03/25 
 */
@Data
@NoArgsConstructor
@SuperBuilder
@EqualsAndHashCode(callSuper = false)
public class Account extends AccountPO {
	public boolean canBalanceSubtract(BigDecimal amount) {
		return BigDecimals.goe(getBalance(), amount);
	}

	public boolean canLockAmountSubtract(BigDecimal amount) {
		return BigDecimals.goe(getLockAmount(), amount);
	}
	
	public boolean canWillIncomeRefund(BigDecimal amount) {
		return BigDecimals.goe(getWillIncomeAmount(), amount);
	}
	
	public AccountThumb thumb() {
		AccountThumb thumb = new AccountThumb();
		Beans.copyPropertiesIgnoreNull(this, thumb);
		thumb.setTradeAcc(ext("tradeAcc", TradeAccThumb.class));
		return thumb;
	}
	
	public JPAUpdateClause limitWithrawPass(BigDecimal amount) {
		QAccountPO q = QAccountPO.accountPO;
		Account account = this;
		
		Expression<BigDecimal> cases = new CaseBuilder()
			     .when(q.limitWithdrawAmount.gt(amount)).then(q.limitWithdrawAmount.subtract(amount))
			     .otherwise(BigDecimal.ZERO);
		
		JPAUpdateClause updateClause = AccountRepository.of()
				.update(q)
				.set(q.limitWithdrawAmount, cases)
				.where(q.id.eq(account.getId()));
		
		setLimitWithdrawAmount(BigDecimals.subtract(getLimitWithdrawAmount(), amount));
		return updateClause;
	}
	
	public JPAUpdateClause oper(AccountLogType accountLogType, BigDecimal amount, AccountOperParam accountOperParam) {
		Account account = this;
		
		QAccountPO q = QAccountPO.accountPO;
		JPAUpdateClause updateClause = AccountRepository.of().update(q)
				.where(q.id.eq(account.getId()));
		
		if(accountLogType == AccountLogType.ADD) {
			updateClause
					.set(q.total, q.total.add(amount))
					.set(q.balance, q.balance.add(amount));
			
			account.setTotal(account.getTotal().add(amount));
			account.setBalance(account.getBalance().add(amount));
		} else if(accountLogType == AccountLogType.SUBTRACT) {
			updateClause
					.set(q.balance, q.balance.subtract(amount));
			
			if(! accountOperParam.isAllowNegative()) {
				if(! account.canBalanceSubtract(amount)) {
					throw new SysTipsException("账号余额不足");
				}
				updateClause.where(q.balance.goe(amount));
			}
			
			account.setBalance(account.getBalance().subtract(amount));
		} else if(accountLogType == AccountLogType.LOCK) {
			if(! account.canBalanceSubtract(amount)) {
				throw new SysTipsException("账号余额不足");
			}
			
			updateClause
					.set(q.lockAmount, q.lockAmount.add(amount))
					.set(q.balance, q.balance.subtract(amount))
					.where(q.balance.goe(amount));
			
			account.setBalance(account.getBalance().subtract(amount));
			account.setLockAmount(account.getLockAmount().add(amount));
		} else if(accountLogType == AccountLogType.LOCK_SUBTRACT) {
			if(! account.canLockAmountSubtract(amount)) {
				throw new SysTipsException("账号冻结金额不足");
			}
			
			updateClause
					.set(q.lockAmount, q.lockAmount.subtract(amount))
					.where(q.lockAmount.goe(amount));
			account.setLockAmount(account.getLockAmount().subtract(amount));
			
			if(AccountActionEnum.WITHDRAWAL == accountOperParam.getAccountAction()) {
				updateClause.set(q.withdrawAmount, q.withdrawAmount.add(amount));
				account.setWithdrawAmount(getWithdrawAmount().add(amount));
			}
		} else if(accountLogType == AccountLogType.LOCK_CANCEL) {
			updateClause
					.set(q.lockAmount, q.lockAmount.subtract(amount))
					.set(q.balance, q.balance.add(amount))
					.where(q.lockAmount.goe(amount));
			
			account.setLockAmount(account.getLockAmount().subtract(amount));
			account.setBalance(account.getBalance().add(amount));
		}  else if(accountLogType == AccountLogType.WILL_INCOME) {
			
			updateClause
					.set(q.willIncomeAmount, q.willIncomeAmount.add(amount));
			
			account.setWillIncomeAmount(account.getWillIncomeAmount().add(amount));
		} else if(accountLogType == AccountLogType.WILL_INCOME_REFUND) {
			if(! account.canWillIncomeRefund(amount)) {
				throw new SysTipsException("即将收入扣除金额不足");
			}
			
			updateClause
					.set(q.willIncomeAmount, q.willIncomeAmount.subtract(amount))
					.where(q.willIncomeAmount.goe(amount));
			account.setWillIncomeAmount(account.getWillIncomeAmount().subtract(amount));
		} else if(accountLogType == AccountLogType.WILL_INCOME_SUCCESS) {
			if(! account.canWillIncomeRefund(amount)) {
				throw new SysTipsException("即将收入扣除金额不足");
			}
			updateClause
				.set(q.total, q.total.add(amount))
				.set(q.balance, q.balance.add(amount))
				
				.set(q.willIncomeAmount, q.willIncomeAmount.subtract(amount))
				.where(q.willIncomeAmount.goe(amount));
			
			account.setTotal(account.getTotal().add(amount));
			account.setBalance(account.getBalance().add(amount));
			account.setWillIncomeAmount(account.getWillIncomeAmount().subtract(amount));
		}
		
		boolean calc = accountLogType == AccountLogType.ADD || accountLogType == AccountLogType.LOCK_SUBTRACT || accountLogType == AccountLogType.SUBTRACT;
		if(calc) {
			if(BigDecimals.gtZero(accountOperParam.getRecharge())) {
				updateClause.set(q.recharge, q.recharge.add(accountOperParam.getRecharge()));
				account.setRecharge(account.getRecharge().add(accountOperParam.getRecharge()));
			}
			
			if(BigDecimals.gtZero(accountOperParam.getCommission())) {
				updateClause.set(q.commission, q.commission.add(accountOperParam.getCommission()));
				account.setCommission(account.getCommission().add(accountOperParam.getCommission()));
			}
			
			if(BigDecimals.gtZero(accountOperParam.getStampTax())) {
				updateClause.set(q.stampTax, q.stampTax.add(accountOperParam.getStampTax()));
				account.setStampTax(account.getStampTax().add(accountOperParam.getStampTax()));
			}
			
			if(BigDecimals.gtZero(accountOperParam.getPreTradingDay())) {
				updateClause.set(q.preTradingDay, q.preTradingDay.add(accountOperParam.getPreTradingDay()));
				account.setPreTradingDay(account.getPreTradingDay().add(accountOperParam.getPreTradingDay()));
			}
		}
		
		if(accountOperParam.getAccountAction() == AccountActionEnum.TRUST_SELL) { //计入 T+0今日,
			
			putExt("T0", BigDecimals.add(t0(), amount));
			updateClause.set(q.ext, getExt());
		}
		
		if(accountOperParam.hasLimitWithraw()) {
			updateClause.set(q.limitWithdrawAmount, q.limitWithdrawAmount.add(amount));
		}
		
		return updateClause;
	}
	
	public BigDecimal t0() {
		return ext("T0", BigDecimal.ZERO);
	}
	
	public BigDecimal t1() {
		return ext("T1", BigDecimal.ZERO);
	}
	
	/**
	 * 沪深可取	T+1交割
		可取=现金额-当日卖出票股票的结算金额-冻结资金-处理中的出金金额
		美港股		T+2交割
		可取=现金额-当日卖出股票的结算金额-昨日卖出股票的结算金额-冻结资金-处理中的出金金额 
	 */
	public BigDecimal sellSettlementAmount() {
		Currency currency = Currency.of(getCurrency());
		if(currency == Currency.CNY) {
			return t0();
		} else if(currency == Currency.HKD || currency == Currency.USD) {
			return BigDecimals.add(t0(), t1());
		} else {
			return BigDecimal.ZERO;
		}
	}
	
	public BigDecimal usableWithdrawAmount() {
		BigDecimal decimal = BigDecimals.subtract(getBalance(), sellSettlementAmount());
		return BigDecimals.loeZero(decimal) ? BigDecimal.ZERO : decimal;
	}
	
//	public void t0ToT1() {
//		putExt("T1", t0());
//		putExt("T0", BigDecimal.ZERO);
//	}

	public JSONObject amountThumb() {
		JSONObject j = new JSONObject();
		j.put("balance", getBalance());
		j.put("lockAmount", getLockAmount());
		j.put("total", getTotal());
		j.put("withdrawAmount", getWithdrawAmount());
		return j;
	}

	public void checkOper(AccountOperParam accountOperParam) {
		if(AccountActionEnum.WITHDRAWAL == accountOperParam.getAccountAction()) {
			if(BooleanEnum.isOff(getWithdrawFlag())) {
				throw new SysTipsException("该账户不能提现.");
			}
		}
	}
	
	public void putExt(String k, Object v) {
		JSONObject json = Jsons.toJSONAnyway(getExt());
		json.put(k, v);
		setExt(json.toJSONString());
	}
	
	public <T> T ext(String k, Class<T> clazz) {
		return Jsons.getByPath(getExt(), k, clazz);
	}
	
	public <T> T ext(String k, T t) {
		return Jsons.getByPath(getExt(), k, t);
	}
}
